/**
 * 
 */
package Verifier.ArithmeticSystem;

import java.math.BigInteger;
import Verifier.Cryptography.PRG;

/**
 * Represents a group. The group can be a sub group of a larger group, but it has to have a creator.
 * 
 * @author Moshe Baavur
 *
 */
public abstract class Group {
		/**
		 * Returns the order of the group. 
		 * 
		 * @return the order of the group which is a probably prime number.
		 */
		abstract public BigInteger getOrder();
		
		/**
		 * Returns the order of the containing group.
		 * 
		 * @return the order of the containing group.
		 */
		abstract public BigInteger getContainingGroupOrder();
		
		/**
		 * Returns the creator of the group as a big integer.
		 * 
		 * @return The creator representation in the group.
		 */
		abstract public BigInteger getGroupCreator();
		
		/**
		 * Returns the creator of the group.
		 * 
		 * @return The creator in the group as an actual element.
		 */
		abstract public GroupElement getGroupCreatorAsGroupElement();
		
		/**
		 * Returns the encoding scheme of the group.
		 * 
		 * @return Number between 0 to 2 which represents the encoding scheme of the group.
		 */
		abstract public int getEncodingScheme();

		/**
		 * Returns the group's identity element.
		 * 
		 * @return The group's identity element in the group.
		 */
		abstract public GroupElement getGroupIdentityElement();

		/**
		 * Checks if the specified element is in the group, meaning the element is a non-negative number and lower than the group order and
		 * meets a criterion to be in the group. For a modular group, the criterion is: element^q = 1 mod p. 
		 * 
		 * @param element the element to check
		 * @return true if and only if the element is a non-negative number which is lower than the group's order and meets 
		 * the criterion to be in the group.
		 */
		abstract public boolean doesElementBelong(GroupElement element);
		
		/**
		 * Compare this to obj. They are equal only if obj is of type Group and their orders, containing Groups and creators are equal.
		 * 
		 * @param obj the object to compare to this.
		 * @return true if and only if obj is of type Group and its order, containing order and creators are equal to this.
		 */
		@Override
		public boolean equals(Object obj) {
			// if the object is a group, then compare them using their order and containing groups and not the address
			if (obj instanceof Group) {
				Group otherGroup = (Group)obj;
				
				return (getOrder().equals(otherGroup.getOrder()) && 
						getGroupCreator().equals(otherGroup.getGroupCreator()) &&
						getContainingGroupOrder().equals(otherGroup.getContainingGroupOrder()));
			}
			// otherwise, they are not equal
			return (false);
		}
		
		/**
		 * Encodes a message into the group, i.e. returns a unique item in the group which represents the message.
		 * 
		 * @param message the message to encode.
		 * @return an element in the group which uniquely identifies the message.
		 * @throws Exception if the message is too long for the encoding scheme of the group.
		 */
		abstract public GroupElement encode(byte[] message) throws Exception;
		
		/**
		 * Decodes an element from the group, i.e. returns a unique message which represents the received element.
		 * 
		 * @param message the element to decode.
		 * @return a byte array which represents the message which was encoded as the element.
		 */
		abstract public byte[] decode(GroupElement message);
		
		/**
		 * Generates an array of elements of the group.
		 * 
		 * @param arraySize the size of the array to generate.
		 * @param pseudoGenerator the PRG to use to generate the elements.
		 * @param seed the seed to use when randomizing using the PRG.
		 * @param error the number of bits that are accepted as an error.
		 * @return array of group elements which were randomly chosen.
		 */
		abstract public GroupElement[] randomArray(int arraySize, PRG pseudoGenerator, byte[] seed, int error);
}
